package com.pig4cloud.pigx.contract.api.v1.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.customize.api.enums.AbnormalEnum;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.api.util.OpenApiUtil;
import com.pig4cloud.pigx.contract.api.v1.service.KLineServiceV1;
import com.pig4cloud.pigx.contract.api.v1.utils.OpenApiContractUtilsV1;
import com.pig4cloud.pigx.contract.constant.ContractKLineConstants;
import com.pig4cloud.pigx.contract.dto.AppContractKLineListDTO;
import com.pig4cloud.pigx.contract.entity.ContractKLine;
import com.pig4cloud.pigx.contract.mapper.ContractKLineMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractKLineMongo;
import com.pig4cloud.pigx.contract.open.api.dto.OpenApiKLineListDTOV1;
import com.pig4cloud.pigx.contract.thread.repair.RepairKLine;
import com.pig4cloud.pigx.contract.utils.HttpUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@AllArgsConstructor
public class KLineServiceImplV1 extends ServiceImpl<ContractKLineMapper, ContractKLine> implements KLineServiceV1 {

	private final MongoTemplate mongo;

	private final RepairKLine repairKLine;

	private final static int LIMIT = 500;

	private final OpenApiContractUtilsV1 utils;


	@Override
	public R<List<List<Object>>> getKLineList(OpenApiKLineListDTOV1 data) {
		if (data.getSymbolName() == null || "".equals(data.getSymbolName())) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.PARAM_EMPTY,"symbolName");
		}
		if (data.getType() == null || "".equals(data.getType())) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.PARAM_EMPTY,"type");
		}
		// 校验币对,币对异常则自动抛出
		utils.getSymbolMongo(data.getSymbolName());
		try {
			// mongo缓存最多500条，如果本次查询量超出范围，则走mysql查询
			Long endTime = data.getEndTime();
			if (endTime != null) {
				LocalDateTime time = Instant.ofEpochMilli(endTime).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
				data.setEndDateTime(time);
			}
			List<ContractKLine> list;
			if (endTime == null) {
				// mongo
				list = this.doMongoSelect(data);
			} else {
				// mysql
				String tableName = Objects.requireNonNull(ContractKLineConstants.findByTimeRangeType(data.getType())).getTableName();
				data.setTableName(tableName);
				AppContractKLineListDTO dto = new AppContractKLineListDTO();
				BeanUtils.copyProperties(data, dto);
				dto.setQuerySize(LIMIT);
				list = baseMapper.selectKLineList(dto);
			}
			// 数据不全上币安补
			try {
				if (list.size() < LIMIT) {
					list = getBiAn(data, list);
				}
			} catch (Exception e) {
				return R.ok(getList(list));
			}
			return R.ok(getList(list));
		} catch (Exception e) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
	}

	private List<List<Object>> getList(List<ContractKLine> lines) {
		List<List<Object>> data = new ArrayList<>();
		for (ContractKLine line : lines) {
			List<Object> list = new ArrayList<>();
			list.add(line.getStartTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
			list.add(line.getOpenPrice());
			list.add(line.getHighPrice());
			list.add(line.getLowPrice());
			list.add(line.getClosePrice());
			list.add(line.getDealNum());
			list.add(line.getEndTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
			// 涨跌额
			BigDecimal subtract = line.getClosePrice().subtract(line.getOpenPrice());
			if (subtract.compareTo(BigDecimal.ZERO) < 0) {
				list.add(subtract.negate());
			} else {
				list.add(subtract);
			}
			// 涨跌幅
			BigDecimal divide = subtract.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).divide(line.getOpenPrice(), 2, RoundingMode.HALF_UP);
			list.add(divide);
			list.add(line.getDealPrice());
			list.add("0");
			list.add("0");
			data.add(list);
		}
		return data;
	}

	private List<ContractKLine> doMongoSelect(OpenApiKLineListDTOV1 data) {
		String collectionName = data.getSymbolName() + "_" + data.getType();
		Query query = new Query();
		query.with(Sort.by(Sort.Order.asc("startTime")));
		if (data.getEndDateTime() != null) {
			query.addCriteria(Criteria.where("startTime").lt(data.getEndDateTime()));
		}
		query.limit(LIMIT);
		List<ContractKLine> kLines = mongo.find(query, ContractKLine.class, collectionName);
		return kLines;
	}

	private List<ContractKLine> getBiAn(OpenApiKLineListDTOV1 data, List<ContractKLine> kLines) {
		List<ContractKLine> list = new ArrayList<>();
		String url = "";
		if (kLines.size() == 0) {
			url = RepairKLine.getUrl(
					data.getSymbolName(),
					Objects.requireNonNull(ContractKLineConstants.findByTimeRangeType(data.getType())).getBiAnLineType(),
					LIMIT,
					null,
					data.getEndTime() - 1
			);
		} else {
			url = RepairKLine.getUrl(
					data.getSymbolName(),
					Objects.requireNonNull(ContractKLineConstants.findByTimeRangeType(data.getType())).getBiAnLineType(),
					LIMIT - kLines.size(),
					null,
					RepairKLine.toTimeStamp(kLines.get(0).getStartTime()) - 1
			);
		}
		List<List<Object>> request;
		try {
			request = (List<List<Object>>) HttpUtils.sendGetRequest(url, new LinkedMultiValueMap<>());
		} catch (Exception e) {
			request = new ArrayList<>();
		}
		request.forEach(e -> {
			ContractKLineMongo lineMongo = repairKLine.getContractKLineMongo(e, null, data.getSymbolName());
			ContractKLine kLine = new ContractKLine();
			BeanUtils.copyProperties(lineMongo, kLine);
			list.add(kLine);
		});
		list.addAll(kLines);
		return list;
	}

}
